package com.rent.writeoff.service.impl;

import com.rent.writeoff.entity.*;
import com.rent.writeoff.service.*;
import com.rent.writeoff.vo.WriteOffResultVO;
import com.rent.writeoff.vo.WriteOffStatisticVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.*;

@Service
@Slf4j
public class BatchWriteOffServiceImpl implements BatchWriteOffService {

    @Autowired
    private BankReceiptService bankReceiptService;

    @Autowired
    private RentPlanService rentPlanService;

    @Autowired
    private WriteOffDetailService writeOffDetailService;

    @Autowired
    private WriteOffSummaryService writeOffSummaryService;

    @Autowired
    private ThreadPoolTaskExecutor writeOffExecutor;

    @Override
    @Transactional
    public WriteOffResultVO executeBatchWriteOff() {
        // 1. 创建核销任务记录
        WriteOffSummary summary = new WriteOffSummary();
        String taskNo = "WO" + LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
                + UUID.randomUUID().toString().substring(0, 6).toUpperCase();
        summary.setTaskNo(taskNo);
        summary.setStartTime(LocalDateTime.now());
        summary.setStatus(0); // 处理中
        writeOffSummaryService.save(summary);

        long startTime = System.currentTimeMillis();
        WriteOffResultVO result = new WriteOffResultVO();
        result.setTaskNo(taskNo);

        try {
            // 2. 获取所有待核销的账户
            List<String> accountNames = bankReceiptService.getDistinctUnusedAccounts();
            if (accountNames.isEmpty()) {
                result.setMessage("无待核销的收款记录");
                summary.setStatus(1); // 完成
                summary.setEndTime(LocalDateTime.now());
                writeOffSummaryService.updateById(summary);
                return result;
            }

            // 3. 多线程处理账户核销
            CompletionService<WriteOffStatisticVO> completionService = new ExecutorCompletionService<>(writeOffExecutor);
            accountNames.forEach(account -> completionService.submit(() -> processSingleAccount(account)));

            // 4. 汇总核销结果
            for (int i = 0; i < accountNames.size(); i++) {
                Future<WriteOffStatisticVO> future = completionService.take();
                WriteOffStatisticVO stat = future.get();
                result.addStatistic(stat);
            }

            // 5. 更新任务状态为完成
            summary.setStatus(1);
            summary.setTotalCount(result.getTotalCount());
            summary.setTotalPrincipal(result.getTotalPrincipal());
            summary.setTotalInterest(result.getTotalInterest());
            result.setMessage("核销成功");

        } catch (Exception e) {
            log.error("批量核销任务失败", e);
            summary.setStatus(2); // 失败
            result.setMessage("核销失败：" + e.getMessage());
        } finally {
            // 6. 计算总耗时并更新任务记录
            long endTime = System.currentTimeMillis();
            BigDecimal totalSeconds = new BigDecimal((endTime - startTime) / 1000.0);
            summary.setEndTime(LocalDateTime.now());
            summary.setTotalSeconds(totalSeconds);
            writeOffSummaryService.updateById(summary);

            result.setTotalTimeSeconds(totalSeconds.doubleValue());
        }

        return result;
    }

    /**
     * 处理单个账户的核销（独立事务）
     */
    @Transactional
    public WriteOffStatisticVO processSingleAccount(String accountName) {
        WriteOffStatisticVO stat = new WriteOffStatisticVO();
        String threadName = Thread.currentThread().getName();
        log.info("线程[{}]开始处理账户[{}]的核销", threadName, accountName);

        try {
            // 1. 获取账户可用收款记录（加锁）
            List<BankReceipt> receipts = bankReceiptService.getUnusedByAccountForUpdate(accountName);
            if (receipts.isEmpty()) {
                log.info("账户[{}]无可用收款记录，跳过", accountName);
                return stat;
            }

            // 2. 获取账户待核销租金计划（加锁）
            List<RentPlan> rentPlans = rentPlanService.getUnwrittenOffByLesseeForUpdate(accountName);
            if (rentPlans.isEmpty()) {
                log.info("账户[{}]无待核销租金计划，跳过", accountName);
                return stat;
            }

            // 3. 计算可用收款总金额
            BigDecimal totalAvailable = receipts.stream()
                    .map(r -> r.getPayAmount().subtract(r.getUsedAmount() == null ? BigDecimal.ZERO : r.getUsedAmount()))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            log.info("账户[{}]可用收款总金额：{}", accountName, totalAvailable);

            // 4. 执行核销逻辑（先利息后本金）
            BigDecimal remainingAmount = totalAvailable;
            for (RentPlan plan : rentPlans) {
                if (remainingAmount.compareTo(BigDecimal.ZERO) <= 0) {
                    break; // 收款金额已用完
                }

                // 计算待核销的利息和本金
                BigDecimal needInterest = plan.getInterest().subtract(
                        plan.getReceivedInterest() == null ? BigDecimal.ZERO : plan.getReceivedInterest()
                );
                BigDecimal needPrincipal = plan.getPrincipal().subtract(
                        plan.getReceivedPrincipal() == null ? BigDecimal.ZERO : plan.getReceivedPrincipal()
                );
                BigDecimal needTotal = needInterest.add(needPrincipal);

                if (needTotal.compareTo(BigDecimal.ZERO) <= 0) {
                    continue; // 该计划已完全核销
                }

                // 本次可核销金额（不超过剩余收款金额）
                BigDecimal writeOffTotal = needTotal.min(remainingAmount);
                // 优先核销利息
                BigDecimal writeOffInterest = writeOffTotal.min(needInterest);
                BigDecimal writeOffPrincipal = writeOffTotal.subtract(writeOffInterest);

                // 5. 更新租金计划
                plan.setReceivedInterest(
                        (plan.getReceivedInterest() == null ? BigDecimal.ZERO : plan.getReceivedInterest()).add(writeOffInterest)
                );
                plan.setReceivedPrincipal(
                        (plan.getReceivedPrincipal() == null ? BigDecimal.ZERO : plan.getReceivedPrincipal()).add(writeOffPrincipal)
                );
                // 更新核销状态
                plan.setWriteOffStatus(calcWriteOffStatus(plan));
                rentPlanService.updateById(plan);

                // 6. 扣减收款记录金额
                BigDecimal remainingWriteOff = writeOffTotal;
                for (BankReceipt receipt : receipts) {
                    if (remainingWriteOff.compareTo(BigDecimal.ZERO) <= 0) {
                        break;
                    }

                    BigDecimal receiptAvailable = receipt.getPayAmount().subtract(
                            receipt.getUsedAmount() == null ? BigDecimal.ZERO : receipt.getUsedAmount()
                    );
                    if (receiptAvailable.compareTo(BigDecimal.ZERO) <= 0) {
                        continue;
                    }

                    BigDecimal useAmount = remainingWriteOff.min(receiptAvailable);
                    receipt.setUsedAmount(
                            (receipt.getUsedAmount() == null ? BigDecimal.ZERO : receipt.getUsedAmount()).add(useAmount)
                    );
                    // 更新使用状态
                    receipt.setUseStatus(calcUseStatus(receipt));
                    bankReceiptService.updateById(receipt);
                    remainingWriteOff = remainingWriteOff.subtract(useAmount);
                }

                // 7. 记录核销明细
                WriteOffDetail detail = new WriteOffDetail();
                detail.setReceiptId(receipts.get(0).getId());
                detail.setRentPlanId(plan.getId());
                detail.setWriteOffPrincipal(writeOffPrincipal);
                detail.setWriteOffInterest(writeOffInterest);
                detail.setWriteOffTime(LocalDateTime.now());
                detail.setThreadName(threadName);
                writeOffDetailService.save(detail);

                // 8. 更新统计信息
                stat.addCount(1);
                stat.addPrincipal(writeOffPrincipal);
                stat.addInterest(writeOffInterest);
                remainingAmount = remainingAmount.subtract(writeOffTotal);
                log.info("账户[{}]核销完成一笔：本金{}，利息{}，剩余收款金额{}",
                        accountName, writeOffPrincipal, writeOffInterest, remainingAmount);
            }

        } catch (Exception e) {
            log.error("账户[{}]核销处理失败", accountName, e);
            throw new RuntimeException("账户[" + accountName + "]核销失败：" + e.getMessage());
        }

        log.info("线程[{}]完成账户[{}]核销，累计{}笔", threadName, accountName, stat.getCount());
        return stat;
    }

    /**
     * 计算租金计划核销状态
     */
    private Integer calcWriteOffStatus(RentPlan plan) {
        BigDecimal receivedInterest = plan.getReceivedInterest() == null ? BigDecimal.ZERO : plan.getReceivedInterest();
        BigDecimal receivedPrincipal = plan.getReceivedPrincipal() == null ? BigDecimal.ZERO : plan.getReceivedPrincipal();

        if (receivedInterest.compareTo(plan.getInterest()) == 0
                && receivedPrincipal.compareTo(plan.getPrincipal()) == 0) {
            return 1; // 已核销
        } else if (receivedInterest.compareTo(BigDecimal.ZERO) > 0
                || receivedPrincipal.compareTo(BigDecimal.ZERO) > 0) {
            return 2; // 部分核销
        } else {
            return 0; // 未核销
        }
    }

    /**
     * 计算收款记录使用状态
     */
    private Integer calcUseStatus(BankReceipt receipt) {
        BigDecimal usedAmount = receipt.getUsedAmount() == null ? BigDecimal.ZERO : receipt.getUsedAmount();
        if (usedAmount.compareTo(receipt.getPayAmount()) == 0) {
            return 1; // 已使用
        } else if (usedAmount.compareTo(BigDecimal.ZERO) > 0) {
            return 2; // 部分使用
        } else {
            return 0; // 未使用
        }
    }
}
